package cz.possoft.serializable.chess.jaxb;

import java.lang.reflect.Constructor;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;

import cz.possoft.converter.jaxb.JAXBGameConverter;
import cz.possoft.serializable.chess.figurky.Figurka;
import cz.possoft.serializable.chess.game.ChessGame;
import cz.possoft.serializable.chess.game.ChessPole;
import cz.possoft.serializable.chess.jaxb.classes.ChessGameType;
import cz.possoft.serializable.chess.jaxb.classes.FigurkaType;
import cz.possoft.serializable.chess.jaxb.classes.LocationType;
import cz.possoft.serializable.chess.jaxb.classes.ObjectFactory;
import cz.possoft.serializable.chess.jaxb.classes.SachovniceType;
import cz.possoft.serializable.generic.Location;

/**
 * 
 * @author mposolda
 */
public class ChessJAXBGameConverter implements JAXBGameConverter<ChessGame> {
	
	private static ChessJAXBGameConverter instance = new ChessJAXBGameConverter();
	
	private ChessJAXBGameConverter() {}
	
	public static ChessJAXBGameConverter getInstance() {
		return instance;
	}

	public JAXBElement<?> convertGameToJAXBElement(ChessGame game) {
		ObjectFactory oFactory = new ObjectFactory();
		ChessGameType chessType = oFactory.createChessGameType();
		
		// Generic
		chessType.setWhitePlayerName(game.getWhitePlayerName());
		chessType.setBlackPlayerName(game.getBlackPlayerName());
		chessType.setWhiteNaTahu(game.isWhiteNaTahu());
		chessType.setMeWhite(game.isMeWhite());
		chessType.setGameBegin(game.isGameBegin());
		chessType.setActive(game.isActive());
		chessType.setEndState(game.isEndState());
		
		// Specific to chess
		chessType.setMelWhiteSach(game.isMelWhiteSach());
		chessType.setMelBlackSach(game.isMelBlackSach());
		chessType.setSachovnice(convertSachovniceToXML(game.getSachovnice(), oFactory));
		
		return oFactory.createChessHame(chessType);
	}

	public ChessGame convertJAXBElementToGame(JAXBElement<?> element) {
		ChessGameType chessType = (ChessGameType)element.getValue(); 
		ChessGame game = new ChessGame();
		
		// Generic
		game.setWhitePlayerName(chessType.getWhitePlayerName());
		game.setBlackPlayerName(chessType.getBlackPlayerName());
		game.setWhiteNaTahu(chessType.isWhiteNaTahu());
		game.setMeWhite(chessType.isMeWhite());
		game.setGameBegin(chessType.isGameBegin());
		game.setActive(chessType.isActive());
		game.setEndState(chessType.isEndState());	
		
		// Specific to chess
		game.setMelWhiteSach(chessType.isMelWhiteSach());
		game.setMelBlackSach(chessType.isMelBlackSach());
		game.setHraciPole(convertXMLToSachovnice(chessType.getSachovnice()));		
		
		return game;
	}

	public JAXBContext getJAXBContext() {
		try {
			return JAXBContext.newInstance("cz.possoft.serializable.chess.jaxb.classes", ChessJAXBGameConverter.class.getClassLoader());
		}
		catch (JAXBException je) {
			je.printStackTrace();
			throw new RuntimeException("Can't create JAXB context.", je);
		}
	}
	
	/* PRIVATE METHODS */
	
	// 1) Methods for converting java types to XML types. 
	
	private SachovniceType convertSachovniceToXML(ChessPole sach, ObjectFactory oFactory) {
		SachovniceType result = oFactory.createSachovniceType();
		result.setLastTah0(sach.getLastTah()[0]);
		result.setLastTah1(sach.getLastTah()[1]);
		result.setLastTah2(sach.getLastTah()[2]);
		result.setLastTah3(sach.getLastTah()[3]);
		result.setBlackKingLocation(convertLocationToXML(sach.getBlackKingLocation(), oFactory));
		result.setWhiteKingLocation(convertLocationToXML(sach.getWhiteKingLocation(), oFactory));
		
		// naplneni figurek
		List<FigurkaType> figurky = result.getFigurka();
		for (int i=0 ; i<8 ; i++)
			for (int j=0 ; j<8 ; j++) {
				Figurka fig = sach.getFigurka(i, j);
				String shortClassName = fig.getClass().getSimpleName();
				// prazdny pole nezapisujeme do XML.
				if ("Prazdna".equals(shortClassName)) {
					continue;
				}
				FigurkaType figType = oFactory.createFigurkaType();
				figType.setType(shortClassName);
				figType.setX(fig.getX());
				figType.setY(fig.getY());
				figType.setColor(fig.getColor());
				figType.setMarkedForPlay(fig.isMarkedForPlay());
				figType.setRing(fig.isRing());
				figType.setOdehralTah(fig.isOdehralTah());
				figurky.add(figType);
			}
		return result;
	}
	
	private LocationType convertLocationToXML(Location loc, ObjectFactory oFactory) {
		LocationType result = oFactory.createLocationType();
		result.setX(loc.getX());
		result.setY(loc.getY());
		return result;
	}
	
	// 2) Methods for converting XML types to java types. 
	
	private ChessPole convertXMLToSachovnice(SachovniceType sachType) {
		ChessPole result = new ChessPole(false);
		result.setLastTah(sachType.getLastTah0(), sachType.getLastTah1(), sachType.getLastTah2(), sachType.getLastTah3());
		result.setBlackKingLocation(convertXMLToLocation(sachType.getBlackKingLocation()));
		result.setWhiteKingLocation(convertXMLToLocation(sachType.getWhiteKingLocation()));
		
		// naplneni figurek
		List<FigurkaType> figurky = sachType.getFigurka();
		for (FigurkaType figType : figurky) {
			String shortClassName = figType.getType();
			try {
				Class<? extends Figurka>  figClass = (Class<? extends Figurka>)Class.forName("cz.possoft.serializable.chess.figurky." + shortClassName);
				Constructor<? extends Figurka> constructor = figClass.getConstructor(int.class, int.class, int.class, ChessPole.class);
				Figurka figurka = constructor.newInstance(figType.getX(), figType.getY(), figType.getColor(), result);
				figurka.setMarked(figType.isMarkedForPlay());
				figurka.setOdehralTah(figType.isOdehralTah());
				figurka.setRing(figType.isRing());
				result.setFigurka(figurka.getX(), figurka.getY(), figurka);
			}
			catch (Exception e) {
				// TODO: some non-runtime exception can be better here.
				throw new RuntimeException("Can't instantiate figurka of type " + shortClassName + ".", e);
			}
		}
		return result;
	}
	
	private Location convertXMLToLocation(LocationType locType) {
		return new Location(locType.getX(), locType.getY());
	}
	

}
